
#include "natv_CoreNative.h"
#include "Register.h"
#include "Mpq.h"
#include "Jpeg.h"

#define INTERPOLATION_TYPE_NONE 0
#define INTERPOLATION_TYPE_LINEAR 1
#define INTERPOLATION_TYPE_HERMITE 2
#define INTERPOLATION_TYPE_BEZIER 3

JNIEXPORT jfloatArray JNICALL Java_natv_CoreNative_matrixMult
(JNIEnv *env, jobject, jfloatArray mat, jfloatArray vec)
{
	D3DXMATRIX matrix = toMatrix(env, mat);
	D3DXVECTOR3 vector = toVector3(env, vec);
	D3DXVECTOR4 temp;
	FLOAT temp3[3];
	
	D3DXVec3Transform(&temp, &vector, &matrix);
	temp3[0] = temp.x;
	temp3[1] = temp.y;
	temp3[2] = temp.z;
	jfloatArray rjta = env->NewFloatArray(3);
	env->SetFloatArrayRegion(rjta, 0, 3, (jfloat *)temp3);
	return rjta;
}

JNIEXPORT jfloatArray JNICALL Java_natv_CoreNative_vec3Scale
(JNIEnv *env, jobject, jfloatArray vec, jfloat scale)
{
	D3DXVECTOR3 vector = toVector3(env, vec);
	D3DXVec3Scale(&vector, &vector, scale);
	FLOAT temp3[3];

	temp3[0] = vector.x;
	temp3[1] = vector.y;
	temp3[2] = vector.z;
	jfloatArray rjta = env->NewFloatArray(3);
	env->SetFloatArrayRegion(rjta, 0, 3, (jfloat *)temp3);
	return rjta;
}

JNIEXPORT jfloatArray JNICALL Java_natv_CoreNative_createMatrix
(JNIEnv *env, jobject, jfloatArray piv,
 jfloat tfactor, jint tlineType, jfloatArray tvec1, jfloatArray tinTan1, jfloatArray toutTan1, jfloatArray tvec2, jfloatArray tinTan2, jfloatArray toutTan2,
 jfloat rfactor, jint rlineType, jfloatArray rvec1, jfloatArray rinTan1, jfloatArray routTan1, jfloatArray rvec2, jfloatArray rinTan2, jfloatArray routTan2,
 jfloat sfactor, jint slineType, jfloatArray svec1, jfloatArray sinTan1, jfloatArray soutTan1, jfloatArray svec2, jfloatArray sinTan2, jfloatArray soutTan2)
{
	D3DXVECTOR3 DefaultTranslationVector = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	D3DXVECTOR4 DefaultVector = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 1.0f);
	D3DXVECTOR3 DefaultScalingVector = D3DXVECTOR3(1.0f, 1.0f, 1.0f);

	D3DXVECTOR3 PivotPoint = toVector3(env,piv);
	D3DXVECTOR3 TranslationVector;
	D3DXVECTOR4 RotationVector;
	D3DXVECTOR3 ScalingVector;

	D3DXVECTOR4 Node1_Vector;
	D3DXVECTOR4 Node2_Vector;
	D3DXVECTOR4 Node1_InTan;
	D3DXVECTOR4 Node1_OutTan;
	D3DXVECTOR4 Node2_InTan;
	D3DXVECTOR4 Node2_OutTan;
	
	if(tfactor != -1) {
		Node1_Vector = tvec1 != NULL ? toVector(env,tvec1) : DefaultVector;
		Node2_Vector = tvec2 != NULL ? toVector(env,tvec2) : DefaultVector;
		Node1_InTan =  tinTan1 != NULL ? toVector(env,tinTan1) : DefaultVector;
		Node1_OutTan = toutTan1 != NULL ? toVector(env,toutTan1) : DefaultVector;
		Node2_InTan = tinTan2 != NULL ? toVector(env,tinTan2) : DefaultVector;
		Node2_OutTan = toutTan2 != NULL ? toVector(env,toutTan2) : DefaultVector;
		TranslationVector = interpol3( tlineType, tfactor, Node1_Vector, Node1_InTan, Node1_OutTan, Node2_Vector, Node2_InTan, Node2_OutTan );
	} else TranslationVector = DefaultTranslationVector;
	
	if(rfactor != -1) {
		Node1_Vector = rvec1 != NULL ? toVector(env,rvec1) : DefaultVector;
		Node2_Vector = rvec2 != NULL ? toVector(env,rvec2) : DefaultVector;
		Node1_InTan = rinTan1 != NULL ? toVector(env,rinTan1) : DefaultVector;
		Node1_OutTan = routTan1 != NULL ? toVector(env,routTan1) : DefaultVector;
		Node2_InTan = rinTan2 != NULL ? toVector(env,rinTan2) : DefaultVector;
		Node2_OutTan = routTan2 != NULL ? toVector(env,routTan2) : DefaultVector;
		RotationVector = interpol4( rlineType, rfactor, Node1_Vector, Node1_InTan, Node1_OutTan, Node2_Vector, Node2_InTan, Node2_OutTan );
	} else RotationVector = DefaultVector;
	
	if(sfactor != -1) {
		Node1_Vector = svec1 != NULL ? toVector(env,svec1) : DefaultVector;
		Node2_Vector = svec2 != NULL ? toVector(env,svec2) : DefaultVector;
		Node1_InTan = sinTan1 != NULL ? toVector(env,sinTan1) : DefaultVector;
		Node1_OutTan = soutTan1 != NULL ? toVector(env,soutTan1) : DefaultVector;
		Node2_InTan = sinTan2 != NULL ? toVector(env,sinTan2) : DefaultVector;
		Node2_OutTan = soutTan2 != NULL ? toVector(env,soutTan2) : DefaultVector;
		ScalingVector = interpol3( slineType, sfactor, Node1_Vector, Node1_InTan, Node1_OutTan, Node2_Vector, Node2_InTan, Node2_OutTan );
	} else ScalingVector = DefaultScalingVector;
	
	D3DXMATRIX matrix;
	D3DXMatrixIdentity(&matrix);
	D3DXMatrixTransformation(&matrix, &PivotPoint, NULL, &ScalingVector, &PivotPoint, reinterpret_cast<D3DXQUATERNION*>(&RotationVector), &TranslationVector);

	return toJFloatArrayM(env, matrix);
}

D3DXVECTOR3 interpol3(int lineType, float Factor, D3DXVECTOR4 Node1_Vector, D3DXVECTOR4 Node1_InTan, D3DXVECTOR4 Node1_OutTan, D3DXVECTOR4 Node2_Vector, D3DXVECTOR4 Node2_InTan, D3DXVECTOR4 Node2_OutTan) {
	D3DXVECTOR4 Vector;
	float InverseFactor = InverseFactor = (1.0f - Factor);

	switch(lineType)
	{
		case INTERPOLATION_TYPE_NONE:
		{
			Vector = Node1_Vector;
			break;
		}

		case INTERPOLATION_TYPE_LINEAR:
		{
			Vector = (InverseFactor * Node1_Vector) + (Factor * Node2_Vector);
			break;
		}

		case INTERPOLATION_TYPE_HERMITE:
		{
			FLOAT Factor1;
			FLOAT Factor2;
			FLOAT Factor3;
			FLOAT Factor4;
			FLOAT FactorTimesTwo;

			FactorTimesTwo = Factor * Factor;

			Factor1 = FactorTimesTwo * (2.0f * Factor - 3.0f) + 1;
			Factor2 = FactorTimesTwo * (Factor - 2.0f) + Factor;
			Factor3 = FactorTimesTwo * (Factor - 1.0f);
			Factor4 = FactorTimesTwo * (3.0f - 2.0f * Factor);

			Vector = (Factor1 * Node1_Vector) + (Factor2 * Node1_OutTan) + (Factor3 * Node2_InTan) + (Factor4 * Node2_Vector);

			break;
		}

		case INTERPOLATION_TYPE_BEZIER:
		{
			FLOAT Factor1;
			FLOAT Factor2;
			FLOAT Factor3;
			FLOAT Factor4;
			FLOAT FactorTimesTwo;
			FLOAT InverseFactorTimesTwo;

			FactorTimesTwo = Factor * Factor;
			InverseFactorTimesTwo = InverseFactor * InverseFactor;

			Factor1 = InverseFactorTimesTwo * InverseFactor;
			Factor2 = 3.0f * Factor * InverseFactorTimesTwo;
			Factor3 = 3.0f * FactorTimesTwo * InverseFactor;
			Factor4 = FactorTimesTwo * Factor;

			Vector = (Factor1 * Node1_Vector) + (Factor2 * Node1_OutTan) + (Factor3 * Node2_InTan) + (Factor4 * Node2_Vector);

			break;
		}
	}
	
	D3DXVECTOR3 rVec;
	rVec.x = Vector.x;
	rVec.y = Vector.y;
	rVec.z = Vector.z;
	return rVec;
}

D3DXVECTOR4 interpol4(int lineType, float Factor, D3DXVECTOR4 Node1_Vector, D3DXVECTOR4 Node1_InTan, D3DXVECTOR4 Node1_OutTan, D3DXVECTOR4 Node2_Vector, D3DXVECTOR4 Node2_InTan, D3DXVECTOR4 Node2_OutTan) {
	D3DXVECTOR4 Vector;
	switch(lineType)
	{
		case INTERPOLATION_TYPE_NONE:
		{
			Vector = Node1_Vector;
			break;
		}

		case INTERPOLATION_TYPE_LINEAR:
		{
			D3DXQUATERNION* Quaternion;
			D3DXQUATERNION* Quaternion1;
			D3DXQUATERNION* Quaternion2;

			Quaternion = reinterpret_cast<D3DXQUATERNION*>(&Vector);
			Quaternion1 = reinterpret_cast<D3DXQUATERNION*>(&Node1_Vector);
			Quaternion2 = reinterpret_cast<D3DXQUATERNION*>(&Node2_Vector);

			D3DXQuaternionSlerp(Quaternion, Quaternion1, Quaternion2, Factor);

			break;
		}

		case INTERPOLATION_TYPE_HERMITE:
		case INTERPOLATION_TYPE_BEZIER:
		{
			D3DXQUATERNION* Quaternion;
			D3DXQUATERNION* Quaternion1;
			D3DXQUATERNION* Quaternion2;
			D3DXQUATERNION* Quaternion3;
			D3DXQUATERNION* Quaternion4;
			D3DXQUATERNION QuaternionA;
			D3DXQUATERNION QuaternionB;
			D3DXQUATERNION QuaternionC;

			Quaternion = reinterpret_cast<D3DXQUATERNION*>(&Vector);
			Quaternion1 = reinterpret_cast<D3DXQUATERNION*>(&Node1_Vector);
			Quaternion2 = reinterpret_cast<D3DXQUATERNION*>(&Node1_InTan);
			Quaternion3 = reinterpret_cast<D3DXQUATERNION*>(&Node2_Vector);
			Quaternion4 = reinterpret_cast<D3DXQUATERNION*>(&Node2_OutTan);

			D3DXQuaternionSquadSetup(&QuaternionA, &QuaternionB, &QuaternionC, Quaternion1, Quaternion2, Quaternion3, Quaternion4);
			D3DXQuaternionSquad(Quaternion, Quaternion1, &QuaternionA, &QuaternionB, &QuaternionC, Factor);

			break;
		}
	}
	return Vector;
}

JNIEXPORT jint JNICALL Java_natv_CoreNative_selectMPQ
(JNIEnv *, jobject, jint mpq) {
	if(mpq == 1) {
		if(MpqWar3.MpqHandle == NULL) return -1;
		Mpq = &MpqWar3;
	} else if(mpq == 2) {
		if(MpqWar3x.MpqHandle == NULL) return -1;
		Mpq = &MpqWar3x;
	}
	/*
	else if(mpq == 3) {
		Mpq = MpqWar3xLocal;
	} else if(mpq == 4) {
		Mpq = MpqWar3Patch;
	}
	*/
	return 0;
}

JNIEXPORT jint JNICALL Java_natv_CoreNative_handleMPQS
(JNIEnv *, jobject, jbyteArray war3path) {
	if(!Register.FindWarcraftDirectory()) {
		return -3;
	}
	
	int size = strlen(Register.WarcraftDirectory);
	char *path = new char[size+20];
	
	strcpy(path, Register.WarcraftDirectory);
	if(path[size-1] != '\\') {
		memcpy(path+size, "\\War3.mpq", 9);
		path[size+9] = NULL;
	} else {
		memcpy(path+size, "War3.mpq", 8);
		path[size+8] = NULL;
	}
	if(!MpqWar3.Open(path)) {free(path); return -1;}

	if(path[size-1] != '\\') {
		memcpy(path+size, "\\War3x.mpq", 10);
		path[size+10] = NULL;
	} else {
		memcpy(path+size, "War3x.mpq", 9);
		path[size+9] = NULL;
	}
	if(!MpqWar3x.Open(path)) {free(path); return -2;}
	return 0;
}

JNIEXPORT jint JNICALL Java_natv_CoreNative_customHandleMPQ
(JNIEnv *env, jobject, jbyteArray war3path) {
	MpqCustom.Close();
	
	int size = env->GetArrayLength(war3path);
	char *path = new char[size+1];
	path[size] = NULL;
	env->GetByteArrayRegion(war3path, 0, size, (jbyte *)path);
	if(!MpqCustom.Open( path )) {free(path); return -1;}
	return 0;
}

JNIEXPORT jbyteArray JNICALL Java_natv_CoreNative_getFileDataFromCustomMPQ
(JNIEnv *env, jobject obj, jbyteArray mpqInFile) {
	if(MpqCustom.MpqHandle == NULL) {
		return NULL;
	}
	int size = env->GetArrayLength(mpqInFile);
	char *fileName = new char[size+1];
	env->GetByteArrayRegion(mpqInFile, 0, size, (jbyte *)fileName);
	fileName[size] = NULL;
	if(MpqCustom.LoadFile(fileName)) {
		free(fileName);
		jbyteArray rjba = env->NewByteArray(MpqCustom.READED);
		env->SetByteArrayRegion(rjba, 0, MpqCustom.READED, (jbyte *)MpqCustom.BUFFER);
		return rjba;
	}
	free(fileName);
	return NULL;
}

JNIEXPORT jbyteArray JNICALL Java_natv_CoreNative_getFileDataFromMPQ
(JNIEnv *env, jobject obj, jbyteArray mpqInFile) {
	if(Mpq == NULL || Mpq->MpqHandle == NULL) {
		return NULL;
	}
	int size = env->GetArrayLength(mpqInFile);
	char *fileName = new char[size+1];
	env->GetByteArrayRegion(mpqInFile, 0, size, (jbyte *)fileName);
	fileName[size] = NULL;
	if(Mpq->LoadFile(fileName)) {
		free(fileName);
		jbyteArray rjba = env->NewByteArray(Mpq->READED);
		env->SetByteArrayRegion(rjba, 0, Mpq->READED, (jbyte *)Mpq->BUFFER);
		return rjba;
	}
	free(fileName);
	return NULL;
}

JNIEXPORT jbyteArray JNICALL Java_natv_CoreNative_toJPEG
(JNIEnv *env, jobject obj, jbyteArray jba) {
	int Size = env->GetArrayLength(jba);
	UCHAR *SourceBuffer = new UCHAR[Size];
	env->GetByteArrayRegion(jba, 0, Size, (jbyte *)SourceBuffer);
	if(!Jpeg.Read(SourceBuffer, Size)) {
		free(SourceBuffer);
		return NULL;
	}
	jbyteArray rjba = env->NewByteArray(Jpeg.InSize);
	env->SetByteArrayRegion(rjba, 0, Jpeg.InSize, (jbyte *)Jpeg.InBuffer);
	free(SourceBuffer);
	return rjba;
}

JNIEXPORT jint JNICALL Java_natv_CoreNative_release
(JNIEnv *, jobject) {
	Jpeg.~JPEG();
	Register.~REGISTER();
	MpqWar3.Close();
	MpqWar3x.Close();
	MpqCustom.Close();
	UnloadStorm();
	return 0;
}

D3DXVECTOR4 toVector(JNIEnv *env,jfloatArray jfa) {
	int jaLen = env->GetArrayLength(jfa);
	float temp[4];;
	
	D3DXVECTOR4 vec4;
	if(jaLen == 3) {
		env->GetFloatArrayRegion( jfa, 0, 3, (jfloat *)temp );
		vec4.x = temp[0];
		vec4.y = temp[1];
		vec4.z = temp[2];
		vec4.w = 0;
	} else {
		env->GetFloatArrayRegion( jfa, 0, 4, (jfloat *)temp );
		vec4.x = temp[0];
		vec4.y = temp[1];
		vec4.z = temp[2];
		vec4.w = temp[3];
	}
	return vec4;
}

D3DXVECTOR3 toVector3(JNIEnv *env,jfloatArray jfa) {
	float temp[3];
	
	D3DXVECTOR3 vec3;
	env->GetFloatArrayRegion( jfa, 0, 3, (jfloat *)temp );
	vec3.x = temp[0];
	vec3.y = temp[1];
	vec3.z = temp[2];

	return vec3;
}


D3DXVECTOR4 toVector4(JNIEnv *env,jfloatArray jfa) {
	float temp[4];
	
	D3DXVECTOR4 vec4;
	env->GetFloatArrayRegion( jfa, 0, 4, (jfloat *)temp );
	vec4.x = temp[0];
	vec4.y = temp[1];
	vec4.z = temp[2];
	vec4.w = temp[3];

	return vec4;
}


jfloatArray toJFloatArrayV(JNIEnv *env,D3DXVECTOR4 vec4) {
	jfloatArray jfa = env->NewFloatArray(4);
	float temp[4];
	temp[0] = vec4.x;
	temp[1] = vec4.y;
	temp[2] = vec4.z;
	temp[3] = vec4.w;
	env->SetFloatArrayRegion( jfa, 0, 4, (jfloat *)temp );
	return jfa;
}

jfloatArray toJFloatArrayV3(JNIEnv *env,D3DXVECTOR4 vec4) {
	jfloatArray jfa = env->NewFloatArray(3);
	float temp[3];
	temp[0] = vec4.x;
	temp[1] = vec4.y;
	temp[2] = vec4.z;
	env->SetFloatArrayRegion( jfa, 0, 3, (jfloat *)temp );
	return jfa;
}

D3DXMATRIX toMatrix(JNIEnv *env,jfloatArray jfa) {
	D3DMATRIX matrix;
	float temp[16];
	env->GetFloatArrayRegion( jfa, 0, 16, (jfloat *)temp );
	matrix._11 = temp[0];
	matrix._12 = temp[1];
	matrix._13 = temp[2];
	matrix._14 = temp[3];

	matrix._21 = temp[4];
	matrix._22 = temp[5];
	matrix._23 = temp[6];
	matrix._24 = temp[7];

	matrix._31 = temp[8];
	matrix._32 = temp[9];
	matrix._33 = temp[10];
	matrix._34 = temp[11];

	matrix._41 = temp[12];
	matrix._42 = temp[13];
	matrix._43 = temp[14];
	matrix._44 = temp[15];
	
	return matrix;
}

jfloatArray toJFloatArrayM(JNIEnv *env,D3DMATRIX matrix) {
	jfloatArray jfa = env->NewFloatArray(16);
	float temp[16];
	
	temp[0] = matrix._11;
	temp[1] = matrix._12;
	temp[2] = matrix._13;
	temp[3] = matrix._14;

	temp[4] = matrix._21;
	temp[5] = matrix._22;
	temp[6] = matrix._23;
	temp[7] = matrix._24;

	temp[8] = matrix._31;
	temp[9] = matrix._32;
	temp[10] = matrix._33;
	temp[11] = matrix._34;

	temp[12] = matrix._41;
	temp[13] = matrix._42;
	temp[14] = matrix._43;
	temp[15] = matrix._44;

	env->SetFloatArrayRegion( jfa, 0, 16, (jfloat *)temp );	
	return jfa;
}

D3DXQUATERNION toQuaternion(JNIEnv *env,jfloatArray jfa) {
	D3DXQUATERNION quat;
	int jfaLen = env->GetArrayLength(jfa);
	float temp[4];
	if(jfaLen == 3) {
		env->GetFloatArrayRegion( jfa, 0, 3, (jfloat *)temp  );
		quat.x = temp[0];
		quat.y = temp[1];
		quat.z = temp[2];
	} else {
		env->GetFloatArrayRegion( jfa, 0, 4, (jfloat *)temp  );
		quat.x = temp[0];
		quat.y = temp[1];
		quat.z = temp[2];
		quat.w = temp[3];
	}
	return quat;
}

jfloatArray toJFloatArrayQ(JNIEnv *env,D3DXQUATERNION quat) {
	jfloatArray jfa = env->NewFloatArray(4);
	float temp[4];
	temp[0] = quat.x;
	temp[1] = quat.y;
	temp[2] = quat.z;
	temp[3] = quat.w;
	env->SetFloatArrayRegion( jfa, 0, 4, (jfloat *)temp );
	return jfa;
}